package com.newy.core.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.newy.core.web.AppContext;
import com.newy.service.sys.UtilService;


/**
 * 实体转换器，允许对实体添加新的字段、修改字段名称和删除字段，或者做一些额外处理。一般在向页面输送实体列表时使用本类。例如：
 * 在向页面传送人员列表List&lt;User&gt; users时，需要给每个User实体添加机构名称字段，可使用如下代码：<br/>
 * new Converter().addField("orgName", "orgId", Orgment.class, "name").toListOfMap(users); 其中，orgName
 * 是新添加的字段名，orgId是User的字段，name是Orgment是字段
 * 
 * @author LinZongxue 2011-10-07
 */
public class Converter {
	/**
	 * 要添加的新字段，key为将要添加到实体中的新属性的名称，value为实现了Convert接口的取值规则。 
	 */
	private Map<String, Convert<Object>> newFields = new HashMap<String, Convert<Object>>();
	/**
	 * 要修改名称的字段，key为原字段名，value为新字段名
	 */
	private Map<String, String> changeFields = new HashMap<String, String>();
	/**
	 * 要删除的字段名称
	 */
	private Set<String> removeFields = new HashSet<String>();
	/**
	 * 要对每个实体进行一些处理的方法
	 */
	private Set<Handle<Object>> handlers = new HashSet<Handle<Object>>();
	/**
	 * 实体类的get方法缓存，结构为：{类:{属性名:属性的get方法}}
	 */
	private static Map<Class<?>, Map<String, Method>> fieldAccessorCache = new ConcurrentHashMap<Class<?>, Map<String,Method>>();
	
	/**
	 * 运用转换规则，将实体列表转换成Map列表。
	 */
	public List<Map<String, Object>> toListOfMap(List<?> entities){
		if (entities == null) return null;
		if (entities.isEmpty()) return new ArrayList<Map<String, Object>>(0);
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(entities.size());
		for (Object entity : entities){
			result.add(toMap(entity));
		}
		return result;
	}
	
	/**
	 * 转换分页对象中的列表为Map列表
	 */
	public Page toListOfMap(Page page){
		Page newPage = new Page();
		newPage.setResult(this.toListOfMap(page.getResult()));
		newPage.setTotalItems(page.getTotalItems());
		return newPage;
	}
	
	/**
	 * 运用转换规则，将实体转换成Map对象
	 */
	public Map<String, Object> toMap(Object entity){
		if (entity == null) return null;
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, Method> fieldAccessors = getFieldAccessors(entity.getClass());
		//先执行要对实体进行处理的方法
		for (Handle<Object> handler : this.handlers){
			handler.handle(entity);
		}
		//处理原实体所有属性
		for (Map.Entry<String, Method> accessor : fieldAccessors.entrySet()){
			try {
				String fieldName = accessor.getKey();
				Object value = accessor.getValue().invoke(entity);
				if (changeFields.get(fieldName) != null){ //改变字段名称
					map.put(changeFields.get(fieldName) , value);
				}
				else if (!removeFields.contains(fieldName)){ //删除字段
					map.put(fieldName, value);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		//添加规则中的属性
		for (Map.Entry<String, Convert<Object>> field : newFields.entrySet()){
			map.put(field.getKey(), field.getValue().to(entity));
		}
		return map;
	}
	
	/**
	 * 添加新字段。value可以为Convert接口对象或者常量
	 * @param fieldName 新字段名称 
	 * @param value Convert接口对象或常量
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Converter addField(String fieldName, Convert convert){
		AssertUtils.hasLength(fieldName, "字段名不能为空");
		newFields.put(fieldName, convert);
		return this;
	}
	
	/**
	 * 添加实体做为新字段
	 * @param fieldName 新字段名称 
	 * @param srcIdField 原实体中，保存目标实体id的字段名
	 * @param targetClass 目标实体的类名
	 * @return
	 */
	public Converter addField(String fieldName, String srcIdField, Class<?> targetClass){
		return this.addField(fieldName, new EntityConverter<Object>(srcIdField, targetClass));
	}
	
	/**
	 * 添加实体的指定字段做为新字段
	 * @param fieldName 新字段名称 
	 * @param srcIdField 原实体中，保存目标实体id的字段名
	 * @param targetClass 目标实体的类名
	 * @param targetField 目标实体的目标字段
	 * @return
	 */
	public Converter addField(String fieldName, String srcIdField, Class<?> targetClass, String targetField){
		return this.addField(fieldName, new EntityConverter<Object>(srcIdField, targetClass, targetField));
	}
	
	public Converter addConstField(String fieldName, Object constant){
		return this.addField(fieldName, new ConstConverter(constant));
	}
	
	/**
	 * 改变字段名称
	 * @param newFileName 新字段名称
	 * @param oldFieldName 原字段名称
	 */
	public Converter changeField(String newFileName, String oldFieldName){
		changeFields.put(oldFieldName, newFileName);
		return this;
	}
	
	/**
	 * 删除字段，可同时删除多个字段
	 * @param fieldNames 要删除的字段名称
	 */
	public Converter removeField(String... fieldNames){
		for (String fieldName : fieldNames){
			removeFields.add(fieldName);
		}
		return this;
	}
	
	/**
	 * 添加要对每个实体进行处理的方法，这个方法将在转换实体前执行。
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Converter addHandler(Handle handler){
		this.handlers.add(handler);
		return this;
	}
	
	/**
	 * 获取类的get访问器，有缓存
	 * @return Map 结构为：{属性名称:属性get方法}
	 */
	private Map<String, Method> getFieldAccessors(Class<?> clazz){
		if (!fieldAccessorCache.containsKey(clazz)){
			fieldAccessorCache.put(clazz, ReflectionUtils.getFieldAccessors(clazz));
		}
		return fieldAccessorCache.get(clazz);
	}
	
	/**
	 * 要对实体进行处理的方法接口
	 * @author LinZongxue
	 *
	 * @param <T> 原实体类
	 */
	public interface Handle<T>{
		public void handle(T src);
	}
	
	/**
	 * 字段转换规则接口。
	 * 实现该接口的to方法，完成实际的转换逻辑。
	 * @author LinZongxue
	 *
	 */
	public interface Convert<T>{
		/**
		 * 字段转换逻辑
		 * @param src 要转换的实体类
		 * @return 目标字段的值
		 */
		public Object to(T src);
	}
	
	/**
	 * 常量转换器
	 * @author LinZongxue
	 */
	private class ConstConverter implements Converter.Convert<Object> {
		private Object constValue;
		/**
		 * 常量转换器
		 * @param constValue 要转换成的常量
		 */
		public ConstConverter(Object constValue){
			this.constValue = constValue;
		}
		@Override
		public Object to(Object src) {
			return constValue;
		}
	}
	
	/**
	 * 实体转换器
	 * @author LinZongxue
	 *
	 */
	private class EntityConverter<T> implements Converter.Convert<T> {
		private UtilService utilMgr = AppContext.getBean(UtilService.class);
		private String srcIdField;
		private Class<?> targetClass;
		private String targetField;
		
		/**
		 * 转换成整个实体
		 * @param srcIdField 原实体中，保存目标实体id的字段名
		 * @param targetClass 目标实体的类名
		 */
		public EntityConverter(String srcIdField, Class<?> targetClass){
			this.srcIdField = srcIdField;
			this.targetClass = targetClass;
		}
		/**
		 * 转换成实体的指定字段
		 * @param srcIdName 原实体中，保存目标实体id的字段名
		 * @param targetClass 目标实体的类名
		 * @param targetField 目标实体的目标字段
		 */
		public EntityConverter(String srcIdField, Class<?> targetClass, String targetField){
			this(srcIdField, targetClass);
			this.targetField = targetField;
		}
		
		@Override
		public Object to(T src) {
			Long srcId = (Long)ReflectionUtils.invokeGetterMethod(src, srcIdField);
			if (srcId == null) return null;
			Object entity = utilMgr.getEntity(targetClass, srcId);
			if (entity == null) return null;
			return targetField == null ? entity : ReflectionUtils.invokeGetterMethod(entity, targetField);
		}
	}
}
